using System.Runtime.CompilerServices;
using System.Collections.Generic;
using System.Threading;

//
// * Copyright (c) 2002-2009 "Neo Technology,"
// *     Network Engine for Objects in Lund AB [http://neotechnology.com]
// *
// * This file is part of Neo4j.
// * 
// * Neo4j is free software: you can redistribute it and/or modify
// * it under the terms of the GNU Affero General Public License as
// * published by the Free Software Foundation, either version 3 of the
// * License, or (at your option) any later version.
// * 
// * This program is distributed in the hope that it will be useful,
// * but WITHOUT ANY WARRANTY; without even the implied warranty of
// * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// * GNU Affero General Public License for more details.
// * 
// * You should have received a copy of the GNU Affero General Public License
// * along with this program. If not, see <http://www.gnu.org/licenses/>.
// 
namespace org.neo4j.kernel.impl.nioneo.store
{


	using LockException = org.neo4j.kernel.impl.transaction.LockException;

///
/// <summary> * Makes a <seealso cref="PersistenceWindow"/> "lockable" meaning it can be locked by a
/// * thread during a operation making sure no other thread use the same window
/// * concurrently. </summary>
/// 
	internal abstract class LockableWindow : PersistenceWindow
	{
		public abstract Buffer getBuffer();

		public abstract long position();

		public abstract int size();

		public abstract void force();

		public abstract void close();

		private OperationType type = null;
		private readonly FileChannel fileChannel;

		private Thread lockingThread = null;
		private readonly LinkedList<Thread> waitingThreadList = new LinkedList<Thread>();
		private int lockCount = 0;
		private int marked = 0;

		internal LockableWindow(FileChannel fileChannel)
		{
			this.fileChannel = fileChannel;
		}

		internal virtual bool encapsulates(long position)
		{
			return position() <= position && position < position() + size();
		}

		internal virtual FileChannel getFileChannel()
		{
			return fileChannel;
		}

		internal virtual OperationType getOperationType()
		{
			return type;
		}

		internal virtual void setOperationType(OperationType type)
		{
			this.type = type;
		}

		[MethodImpl(MethodImplOptions.Synchronized)]
		internal virtual void mark()
		{
			this.marked++;
		}

		[MethodImpl(MethodImplOptions.Synchronized)]
		internal virtual bool isMarked()
		{
			return marked > 0;
		}

		[MethodImpl(MethodImplOptions.Synchronized)]
		internal virtual void @lock()
		{
			Thread currentThread = Thread.CurrentThread;
			while (lockCount > 0 && lockingThread != currentThread)
			{
				waitingThreadList.AddFirst(currentThread);
				try
				{
					wait();
				}
				catch (InterruptedException e)
				{
					Thread.interrupted();
				}
			}
			lockCount++;
			lockingThread = currentThread;
			marked--;
		}

		[MethodImpl(MethodImplOptions.Synchronized)]
		internal virtual void unLock()
		{
			Thread currentThread = Thread.CurrentThread;
			if (lockCount == 0)
			{
				throw new LockException("" + currentThread + " don't have window lock on " + this);
			}
			lockCount--;
			if (lockCount == 0)
			{
				lockingThread = null;
				if (waitingThreadList.Count > 0)
				{
					waitingThreadList.RemoveLast().interrupt();
				}
			}
		}

		[MethodImpl(MethodImplOptions.Synchronized)]
		internal virtual int getWaitingThreadsCount()
		{
			return waitingThreadList.Count;
		}
	}
}